/*
 * thread_scheduler.h
 *
 *  Created on: Oct 17, 2012
 *      Author: tiberius
 */

#ifndef THREAD_SCHEDULER_H_
#define THREAD_SCHEDULER_H_

#include <iostream>
#include <string>
#include <vector>

#include <pthread.h>
#include <semaphore.h>

using namespace std;

template<typename MODULE>
struct thread_data {
	MODULE* mod;
	void (MODULE::*run)();
	void (MODULE::*run_with_args)(void*);
	// TODO : Add void* (MODULE::*run_with_args_and_return)(void*)
	void* args;

	thread_data() {
		mod = NULL;
		run = NULL;
		run_with_args = NULL;
		args = NULL;
	}
};

template<typename MODULE>
void* redirect_thread_handle_run(void*m) {
	thread_data<MODULE>* td = (thread_data<MODULE>*) m;
	void* args = td->args;
	MODULE* mod = td->mod;
	void (MODULE::*run_with_args)(void*)=td->run_with_args;
	void (MODULE::*run)()=td->run;
	if (run != NULL) {
		(mod->*run)();
	} else if (run_with_args != NULL) {
		(mod->*run_with_args)(args);
	}
	return NULL;
}

template<typename MODULE>
struct thread_handle {
	// Attributes
	pthread_t* handle;
	string name;
	bool set_stack_size;
	bool is_joinable;
	size_t stacksize;
	// Return attribute structure
	pthread_attr_t* get_attr() {
		pthread_attr_t* result = new pthread_attr_t();
		pthread_attr_init(result);
		if (set_stack_size)
			pthread_attr_setstacksize(result, stacksize);
		if (is_joinable)
			pthread_attr_setdetachstate(result, PTHREAD_CREATE_JOINABLE);
		return result;
	}
	// Cancel the thread
	void cancel() {
		int rc;
		rc = pthread_cancel(*handle);
		if (rc)
			cout << "Error : [thread_handle : " << name << ".cancel()]" << endl
					<< "Thread canceling not successful. Error code: " << rc
					<< endl;
		cout.flush();
	}
	// Joid the thread
	void join() {
		int rc;
		void* status;
		rc = pthread_join(*handle, &status);
		if (rc)
			cout << "Error : [thread_handle : " << name << ".join()]" << endl
					<< "Thread joining not successful. Error code: " << rc
					<< endl;
		cout.flush();
	}
	// Detach the thread
	void detach() {
		int rc;
		rc = pthread_detach(*handle);
		if (rc)
			cout << "Error : [thread_handle : " << name << ".detach()]" << endl
					<< "Thread detach not successful. Error code: " << rc
					<< endl;
		cout.flush();
	}

	// Thread data
	MODULE* mod;
	void (MODULE::*run)();
	void (MODULE::*run_with_args)(void*);

	// Start thread function
	void start() {
		thread_data<MODULE>* td = new thread_data<MODULE>();
		pthread_attr_t* attr = this->get_attr();
		int rc;
		td->mod = mod;
		td->run = run;
		rc = pthread_create(handle, attr, redirect_thread_handle_run<MODULE>,
				(void*) td);
		if (rc)
			cout << "Error : [thread_handle : " << name << ".start()]" << endl
					<< "Thread creation not successful. Error code: " << rc
					<< endl;
		cout.flush();
		rc = pthread_attr_destroy(attr);
		if (attr != NULL)
			delete attr;
	}
	void start(void* args) {
		thread_data<thread_handle<MODULE> >* td = new thread_data<
				thread_handle<MODULE> >();
		pthread_attr_t* attr = this->get_attr();
		int rc;
		td->mod = mod;
		td->run_with_args = run_with_args;
		td->args = args;
		rc = pthread_create(handle, attr, redirect_thread_handle_run<MODULE>,
				(void*) td);
		if (rc)
			cout << "Error : [thread_handle : " << name << ".start(void*)]"
					<< endl << "Thread creation not successful. Error code: "
					<< rc << endl;
		rc = pthread_attr_destroy(attr);
		if (attr != NULL)
			delete attr;
	}
	// Constructor
	void reset() {
		handle = new pthread_t();
		set_stack_size = false;
		is_joinable = true;
		mod = NULL;
		run = NULL;
		run_with_args = NULL;
	}
	thread_handle() {
		reset();
	}
	thread_handle(MODULE* m, void (MODULE::*r)(), bool run_now=true) {
		reset();
		mod = m;
		run = r;
		if(run_now) this->start();
	}
	thread_handle(MODULE* m, void (MODULE::*rwa)(void*), bool run_now=true, void* a=NULL) {
		reset();
		mod = m;
		run_with_args = rwa;
		if(run_now) {
			this->start(a);
		}
	}
	thread_handle(pthread_t* h) {
		reset();
		handle = h;
	}
protected:
	// Destructor
	~thread_handle() {
		pthread_cancel(*handle);
		if (handle) {
			delete handle;
		}
	}
};

struct mutex_handle {
	void lock() {
		int rc;
		rc = pthread_mutex_lock(handle);
		// TODO : error messages in all mutex functions
		if (rc)
			cout << "" << endl;
	}

	void unlock() {
		int rc;
		rc = pthread_mutex_lock(handle);
		if (rc)
			cout << "" << endl;
	}

	bool try_lock() {
		int rc;
		rc = pthread_mutex_trylock(handle);
		return rc==0;
	}

	mutex_handle() {
		handle = new pthread_mutex_t();
		pthread_mutex_init(handle, NULL);
	}
	mutex_handle(pthread_mutex_t* h) {
		handle = h;
		pthread_mutex_init(handle, NULL);
	}
	~mutex_handle() {
		pthread_mutex_destroy(handle);
		if (handle) {
			delete handle;
		}
	}
	pthread_mutex_t* get_handle() {
		return handle;
	}
protected:
	pthread_mutex_t* handle;
};

struct event_handle {
	// TODO : error messages in all event functions
	void emit() {
		mhandle->lock();
		pthread_cond_signal(handle);
		mhandle->unlock();
	}

	void wait() {
		mhandle->lock();
		pthread_cond_wait(handle,mhandle->get_handle());
		mhandle->unlock();
	}
	event_handle() {
		handle = new pthread_cond_t();
		mhandle = new mutex_handle();
	}
	event_handle(mutex_handle* mh) {
		handle = new pthread_cond_t();
		mhandle = mh;
	}
	~event_handle() {
		if(mhandle )delete mhandle;
		pthread_cond_destroy(handle);
		if(handle) {
			delete handle;
		}
	}
protected:
	pthread_cond_t* handle;
	mutex_handle* mhandle;
};

// TODO : add semaphores
// TODO : test threads with args
// TODO : test mutexes
// TODO : see if mutex attributes are needed
// TODO : test events
// TODO :  add name fields to all classes and constructors
// TODO : thread creation macros
// TODO : overload = operatior to use pthread_equals
// TODO : use pthread_self()
#endif /* THREAD_SCHEDULER_H_ */
